package com.pfzy.utildemo.activity.record.sdk.pcm.aac;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.os.Process;

import com.pfzy.lib.config.log.MyLog;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * AAC录音助手
 */
public class AACRecordHelper {
    public static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;
    private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT; // 编码
    private static AACRecordHelper instance;
    private AACEncoder aacEncoder;
    private AudioRecord audioRecord;
    private int bufferSize;
    private FileOutputStream os;
    private int sampleRate;
    private int source;
    private static final int[] SOURCE = {1, 6};
    private static final int[] SAMPLE_RATES_HZ = {44100, 16000, 11025, 8000};
    private volatile boolean isRecording = false;
    private ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    private ExecutorService audioDataThreadExecutor = Executors.newSingleThreadExecutor();
    private LinkedBlockingQueue<Runnable> mRunnables = new LinkedBlockingQueue<>();

    private AACRecordHelper() {
    }

    public static AACRecordHelper getInstance() {
        if (instance == null) {
            instance = new AACRecordHelper();
        }
        return instance;
    }

    /**
     * 启动
     */
    public void start(String path) {
        _logd("startRecord path : " + path);
        if (!this.isRecording) {
            this.isRecording = true;
            try {
                this.os = new FileOutputStream(path);
                this.audioDataThreadExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        while (isRecording && !Thread.interrupted()) {
                            try {
                                Runnable runnable = mRunnables.take();
                                runnable.run();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        mRunnables.clear();
                        _logd("文件存储线程退出...");
                    }
                });

                calRecordConfig();
                this.aacEncoder = new AACEncoder();
                this.aacEncoder.setCallback(new AACEncoder.Callback() {
                    /**
                     * 输出音频数据
                     * @param aac
                     * @param len
                     * @param nTimeStamp
                     */
                    @Override
                    public void outputAudioData(final byte[] aac, final int len, int nTimeStamp) {
                        if (os != null) {
                            try {
                                mRunnables.put(new Runnable() {
                                    @Override
                                    public void run() {
                                        if (os != null) {
                                            try {
                                                os.write(aac, 0, len);
                                                os.flush();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }
                                });
                            } catch (InterruptedException e) {
                                _loge("outputAudioData===== error " + e);
                                e.printStackTrace();
                            }
                        }
                    }
                });
                this.aacEncoder.start();
                this.audioRecord = new AudioRecord(this.source, this.sampleRate, CHANNEL, ENCODING, this.bufferSize);
                if (this.audioRecord.getState() != 1) {
                    _logd("uninitialized AudioRecord");
                    this.audioRecord = null;
                    stop();
                    throw new IllegalStateException("startRecording() -- called on an uninitialized AudioRecord");
                }
                this.audioRecord.startRecording();
                startPullingData();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 计算记录配置
     */
    private void calRecordConfig() {
        int tmpBufferSize = 0;
        int tmpSampleRate = 0;
        int tmpSource = 0;

        for (int i = 0; i < SOURCE.length; i++) {
            int source = SOURCE[i];
            for (int rate : SAMPLE_RATES_HZ) {
                tmpBufferSize = AudioRecord.getMinBufferSize(rate, CHANNEL, ENCODING);
                AudioRecord testAudioRecord = new AudioRecord(source, rate, CHANNEL, ENCODING, tmpBufferSize);
                if (testAudioRecord.getState() == 1) {
                    testAudioRecord.release();
                    tmpSampleRate = rate;
                    tmpSource = source;
                    break;
                }
            }
        }

        if (tmpBufferSize == 0 || tmpSampleRate == 0) {
            _loge("Failed to initialize");
        }
        this.bufferSize = Math.min(4096, tmpBufferSize);
        this.sampleRate = tmpSampleRate;
        this.source = tmpSource;
        _logd("Sample rate = " + this.sampleRate + "Hz, Source = " + this.source + " (VOICE_RECOGNITION = 6 , MIC = 1)");
    }

    /**
     * 停止
     */
    public void stop() {
        _logd("stopRecord");
        if (this.isRecording) {
            this.isRecording = false;
            if (this.audioRecord != null) {
                this.audioRecord.stop();
                this.audioRecord.release();
                this.audioRecord = null;
            }
            if (this.os != null) {
                try {
                    this.os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (this.aacEncoder != null) {
                this.aacEncoder.stop();
            }
        }
    }

    /**
     * 更改新记录路径
     */
    public void changeNewRecordPath(String recordPath) {
        if (isRecording()) {
            _logd("changeNewRecordPath recordPath: " + recordPath);
            try {
                if (this.os != null) {
                    this.os.close();
                }
                this.os = new FileOutputStream(recordPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 开始拉动数据
     */
//    @SuppressLint({"ThreadDetector"})
    private void startPullingData() {
        this.singleThreadExecutor.execute(new Runnable() {
            @Override
            public void run() {
                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
                byte[] data = new byte[bufferSize];
                while (isRecording) {
                    int read = audioRecord.read(data, 0, bufferSize);
                    if (read > 0) {
                        aacEncoder.putAudioData(data);// 存入音频数据
                        continue;
                    } else if (read <= 0) {
                        return;
                    }
                }
            }
        });
    }

    /**
     * 获取采样率
     */
    public int getSampleRate() {
        return this.sampleRate;
    }

    /**
     * 获取音频源
     */
    public int getAudioSource() {
        return this.source;
    }

    /**
     * 正在录制
     */
    public boolean isRecording() {
        return this.isRecording;
    }

    private void _logd(String msg) {
        MyLog.logd("AAC录音助手 " + msg);
    }

    private void _loge(String msg) {
        MyLog.loge("AAC录音助手 " + msg);
    }
}
